﻿using System;
using System.Data;
using System.Collections.Generic;
using CounterServerModel.Exceptions;
using CounterServerModel.Authentication;

namespace CounterServerModel
{
    /// <summary>
    /// A class that allows to update information about users, accounts, and biils.
    /// </summary>
    public class InformationUpdater
    {
        private AuthenticationService m_AuthenticationService;

        public InformationUpdater()
        {
            m_AuthenticationService = new AuthenticationService();
        }

        /// <summary>
        /// Creates an Account.
        /// </summary>
        /// <param name="i_AuthenticatedUserCredentials">The credentials of the user performing the operation.</param>
        /// <param name="i_AccountInfo">The information of the account.</param>
        /// <returns>The ID of the created account.</returns>
        public int CreateAccount(AuthenticatedUserCredentials i_AuthenticatedUserCredentials, AccountInfo i_AccountInfo)
        {
            m_AuthenticationService.ValidateAuthentication(i_AuthenticatedUserCredentials);

            DataRow rowOfSearchingUser = GetRowOfUser(i_AuthenticatedUserCredentials.ID);

            eUserPermissions userPermissions = (eUserPermissions)int.Parse(rowOfSearchingUser["Permissions"].ToString());

            if (!((userPermissions & eUserPermissions.CreateAccount) == eUserPermissions.CreateAccount))
            {
                throw new NotEnoughPrivilagesException(i_AuthenticatedUserCredentials.ID);
            }

            string commandString = string.Format(
                "INSERT INTO Accounts (Country, City, Street, House{6}, Phone, Longitude, Latitude) VALUES ('{0}', '{1}', '{2}', '{3}'{4}, '{5}', '{7}', '{8}');",
                i_AccountInfo.Country,
                i_AccountInfo.City,
                i_AccountInfo.Street,
                i_AccountInfo.House,
                i_AccountInfo.Apartment == null ? string.Empty : string.Format(", '{0}'", i_AccountInfo.Apartment),
                i_AccountInfo.Phone,
                i_AccountInfo.Apartment == null ? string.Empty : ", Apartment",
                i_AccountInfo.Longitude,
                i_AccountInfo.Latitude);

            DBService.Instance.ExecuteCommand(commandString);

            string selectString = string.Format(
                "SELECT AccountID FROM Accounts WHERE Country = '{0}' AND City = '{1}' AND Street = '{2}' AND House = '{3}'{4};",
                i_AccountInfo.Country,
                i_AccountInfo.City,
                i_AccountInfo.Street,
                i_AccountInfo.House,
                i_AccountInfo.Apartment == null ? string.Empty : string.Format(" AND Apartment = '{0}'", i_AccountInfo.Apartment));

            DataTable accountsTable = DBService.Instance.PerformQuery(selectString);

            int accountID = int.Parse(accountsTable.Rows[0]["AccountID"].ToString());

            foreach (int userID in i_AccountInfo.UsersAssociatedWith)
            {
                string insertString = string.Format("INSERT INTO [Accounts-Users] (AccountID, UserID) VALUES ('{0}', '{1}');", accountID, userID);
                DBService.Instance.ExecuteCommand(insertString);
            }

            return accountID;
        }

        /// <summary>
        /// Deletes an account.
        /// </summary>
        /// <param name="i_AuthenticatedUserCredentials">The credentials of the user performing the operation.</param>
        /// <param name="i_AccountInfo">The information of the account.</param>
        public void DeleteAccount(AuthenticatedUserCredentials i_AuthenticatedUserCredentials, AccountInfo i_AccountInfo)
        {
            m_AuthenticationService.ValidateAuthentication(i_AuthenticatedUserCredentials);

            DataRow rowOfSearchingUser = GetRowOfUser(i_AuthenticatedUserCredentials.ID);

            eUserPermissions userPermissions = (eUserPermissions)int.Parse(rowOfSearchingUser["Permissions"].ToString());

            if (!((userPermissions & eUserPermissions.DeleteAccount) == eUserPermissions.DeleteAccount))
            {
                throw new NotEnoughPrivilagesException(i_AuthenticatedUserCredentials.ID);
            }

            string commandString = string.Format("DELETE FROM Accounts WHERE [AccountID]={0}",i_AccountInfo.AccountID);

            DBService.Instance.ExecuteCommand(commandString);
        }

        /// <summary>
        /// Deletes an account.
        /// </summary>
        /// <param name="i_AuthenticatedUserCredentials">The credentials of the user performing the operation.</param>
        /// <param name="i_AccountInfo">The information of the account.</param>
        public void DeleteAccount(AuthenticatedUserCredentials i_AuthenticatedUserCredentials, int i_AccountID)
        {
            m_AuthenticationService.ValidateAuthentication(i_AuthenticatedUserCredentials);

            DataRow rowOfSearchingUser = GetRowOfUser(i_AuthenticatedUserCredentials.ID);

            eUserPermissions userPermissions = (eUserPermissions)int.Parse(rowOfSearchingUser["Permissions"].ToString());

            if (!((userPermissions & eUserPermissions.DeleteAccount) == eUserPermissions.DeleteAccount))
            {
                throw new NotEnoughPrivilagesException(i_AuthenticatedUserCredentials.ID);
            }

            string commandString = string.Format("DELETE FROM Accounts WHERE AccountID = '{0}';", i_AccountID);
            DBService.Instance.ExecuteCommand(commandString);
        }

        /// <summary>
        /// Creates a user.
        /// </summary>
        /// <param name="i_AuthenticatedUserCredentials">The credentials of the user performing the operation.</param>
        /// <param name="i_UserInfo">The information of the user.</param>
        /// <returns>The ID of the created user.</returns>
        public int CreateUser(AuthenticatedUserCredentials i_AuthenticatedUserCredentials, UserInfo i_UserInfo)
        {
            m_AuthenticationService.ValidateAuthentication(i_AuthenticatedUserCredentials);

            DataRow rowOfSearchingUser = GetRowOfUser(i_AuthenticatedUserCredentials.ID);

            eUserPermissions userPermissions = (eUserPermissions)int.Parse(rowOfSearchingUser["Permissions"].ToString());

            if (!((userPermissions & eUserPermissions.CreateUser) == eUserPermissions.CreateUser))
            {
                throw new NotEnoughPrivilagesException(i_AuthenticatedUserCredentials.ID);
            }

            string commandString = string.Format(
                "INSERT INTO Users (Username, Permissions, Phone, MobilePhone, Email, Password) VALUES ('{0}', '{1}', '{2}', '{3}', '{4}', '{5}');",
                i_UserInfo.Username,
                (int)i_UserInfo.UserPermissions,
                i_UserInfo.Phone,
                i_UserInfo.MobilePhone,
                i_UserInfo.Email,
                i_UserInfo.Password);

            DBService.Instance.ExecuteCommand(commandString);

            string selectString = string.Format(
                "SELECT UserID FROM Users WHERE Username = '{0}';",
                i_UserInfo.Username);

            DataTable usersTable = DBService.Instance.PerformQuery(selectString);

            int userID = int.Parse(usersTable.Rows[0]["UserID"].ToString());

            foreach (int accountID in i_UserInfo.AccountsAssociatedWith)
            {
                string insertString = string.Format("INSERT INTO [Accounts-Users] (AccountID, UserID) VALUES ('{0}', '{1}');", accountID, userID);
                DBService.Instance.ExecuteCommand(insertString);
            }

            return userID;
        }

        /// <summary>
        /// Deletes a user.
        /// </summary>
        /// <param name="i_AuthenticatedUserCredentials">The credentials of the user performing the operation.</param>
        /// <param name="i_UserID">The information of the user.</param>
        public void DeleteUser(AuthenticatedUserCredentials i_AuthenticatedUserCredentials, int i_UserID)
        {
            m_AuthenticationService.ValidateAuthentication(i_AuthenticatedUserCredentials);

            DataRow rowOfSearchingUser = GetRowOfUser(i_AuthenticatedUserCredentials.ID);

            eUserPermissions userPermissions = (eUserPermissions)int.Parse(rowOfSearchingUser["Permissions"].ToString());

            if (!((userPermissions & eUserPermissions.DeleteUser) == eUserPermissions.DeleteUser))
            {
                throw new NotEnoughPrivilagesException(i_AuthenticatedUserCredentials.ID);
            }

            string commandString = string.Format("DELETE FROM Users WHERE UserID = '{0}';", i_UserID);
            DBService.Instance.ExecuteCommand(commandString);
        }

        /// <summary>
        /// Updates an Account.
        /// </summary>
        /// <param name="i_AuthenticatedUserCredentials">The credentials of the user performing the operation.</param>
        /// <param name="i_AccountInfo">The information of the account.</param>
        public void UpdateAccount(AuthenticatedUserCredentials i_AuthenticatedUserCredentials, AccountInfo i_AccountInfo)
        {
            m_AuthenticationService.ValidateAuthentication(i_AuthenticatedUserCredentials);

            bool allowOperation = false;

            DataRow rowOfSearchingUser = GetRowOfUser(i_AuthenticatedUserCredentials.ID);

            eUserPermissions userPermissions = (eUserPermissions)int.Parse(rowOfSearchingUser["Permissions"].ToString());

            if ((userPermissions & eUserPermissions.EditOtherAccountInfo) == eUserPermissions.EditOtherAccountInfo)
            {
                allowOperation = true;
            }
            else
            {
                DataTable tableOfAccountsOfUser = DBService.Instance.PerformQuery(string.Format("SELECT AccountID FROM [Accounts-Users] WHERE UserID = '{0}'", i_AuthenticatedUserCredentials.ID));

                bool isAssociated = false;
                for (int i = 0; (i < tableOfAccountsOfUser.Rows.Count) && !isAssociated; i++)
                {
                    if (int.Parse(tableOfAccountsOfUser.Rows[i]["AccountID"].ToString()) == i_AccountInfo.AccountID)
                    {
                        isAssociated = true;
                    }
                }

                if (isAssociated && ((userPermissions & eUserPermissions.EditAssociatedAccountInfo) == eUserPermissions.EditAssociatedAccountInfo))
                {
                    allowOperation = true;
                }
            }

            if (!allowOperation)
            {
                throw new NotEnoughPrivilagesException(i_AuthenticatedUserCredentials.ID);
            }

            string commandString1 = string.Format("DELETE FROM [Accounts-Users] WHERE AccountID = '{0}';", i_AccountInfo.AccountID.ToString());
            DBService.Instance.ExecuteCommand(commandString1);

            string commandString = string.Format(
                "UPDATE Accounts SET Country = '{0}', City = '{1}', Street = '{2}', House = '{3}'{4}, Phone = '{5}', Longitude = '{7}', Latitude = '{8}' WHERE AccountID = '{6}';",
                i_AccountInfo.Country,
                i_AccountInfo.City,
                i_AccountInfo.Street,
                i_AccountInfo.House,
                i_AccountInfo.Apartment == null ? string.Empty : string.Format(", Apartment = '{0}'", i_AccountInfo.Apartment),
                i_AccountInfo.Phone,
                i_AccountInfo.AccountID,
                i_AccountInfo.Longitude,
                i_AccountInfo.Latitude);

            DBService.Instance.ExecuteCommand(commandString);

            foreach (int userID in i_AccountInfo.UsersAssociatedWith)
            {
                string insertString = string.Format("INSERT INTO [Accounts-Users] (AccountID, UserID) VALUES ('{0}', '{1}');", i_AccountInfo.AccountID, userID);
                DBService.Instance.ExecuteCommand(insertString);
            }
        }

        /// <summary>
        /// Updates a user.
        /// </summary>
        /// <param name="i_AuthenticatedUserCredentials">The credentials of the user performing the operation.</param>
        /// <param name="i_UserInfo">The information of the user.</param>
        public void UpdateUser(AuthenticatedUserCredentials i_AuthenticatedUserCredentials, UserInfo i_UserInfo)
        {
            m_AuthenticationService.ValidateAuthentication(i_AuthenticatedUserCredentials);

            bool allowOperation = false;

            DataRow rowOfSearchingUser = GetRowOfUser(i_AuthenticatedUserCredentials.ID);

            eUserPermissions userPermissions = (eUserPermissions)int.Parse(rowOfSearchingUser["Permissions"].ToString());

            if ((userPermissions & eUserPermissions.EditOtherUserInfo) == eUserPermissions.EditOtherUserInfo)
            {
                allowOperation = true;
            }
            else
            {
                if (i_AuthenticatedUserCredentials.ID == i_UserInfo.ID && ((userPermissions & eUserPermissions.EditOwnUserInfo) == eUserPermissions.EditOwnUserInfo))
                {
                    allowOperation = true;
                }
            }

            if (!allowOperation)
            {
                throw new NotEnoughPrivilagesException(i_AuthenticatedUserCredentials.ID);
            }

            string commandString1 = string.Format("DELETE FROM [Accounts-Users] WHERE UserID = '{0}';", i_UserInfo.ID.ToString());
            DBService.Instance.ExecuteCommand(commandString1);

            string commandString = string.Format(
                "UPDATE Users SET Username = '{0}', Permissions = '{1}', Phone = '{2}', MobilePhone = '{3}', Email = '{4}', Password = '{5}' WHERE UserID = '{6}';",
                i_UserInfo.Username,
                (int)i_UserInfo.UserPermissions,
                i_UserInfo.Phone,
                i_UserInfo.MobilePhone,
                i_UserInfo.Email,
                i_UserInfo.Password,
                i_UserInfo.ID);

            DBService.Instance.ExecuteCommand(commandString);

            foreach (int accountID in i_UserInfo.AccountsAssociatedWith)
            {
                string insertString = string.Format("INSERT INTO [Accounts-Users] (AccountID, UserID) VALUES ('{0}', '{1}');", accountID, i_UserInfo.ID);
                DBService.Instance.ExecuteCommand(insertString);
            }
        }

        /// <summary>
        /// Updates usage of a counter (account).
        /// </summary>
        /// <param name="i_AuthenticatedUserCredentials">The credentials of the user performing the operation.</param>
        /// <param name="i_AccountID">The ID of the account.</param>
        /// <param name="i_TotalUsage">The total usage of the account.</param>
        public void UpdateUsage(AuthenticatedUserCredentials i_AuthenticatedUserCredentials, int i_AccountID, double i_TotalUsage)
        {
            m_AuthenticationService.ValidateAuthentication(i_AuthenticatedUserCredentials);

            DataRow rowOfSearchingUser = GetRowOfUser(i_AuthenticatedUserCredentials.ID);

            eUserPermissions userPermissions = (eUserPermissions)int.Parse(rowOfSearchingUser["Permissions"].ToString());

            if (!((userPermissions & eUserPermissions.UpdateUsage) == eUserPermissions.UpdateUsage))
            {
                throw new NotEnoughPrivilagesException(i_AuthenticatedUserCredentials.ID);
            }

            DataTable table = DBService.Instance.PerformQuery(string.Format("SELECT AccountID FROM Accounts WHERE AccountID = '{0}'", i_AccountID));

            if (table.Rows.Count == 0)
            {
                throw new AccountIDNotExistsException(i_AccountID);
            }
   
            string insertString = string.Format("INSERT INTO [Usages] (AccountID, UpdateDate, TotalUsage) VALUES ('{0}', '{1}', '{2}');", i_AccountID, DateTime.Now, i_TotalUsage);
            DBService.Instance.ExecuteCommand(insertString);
        }

        /// <summary>
        /// Creates a fault.
        /// </summary>
        /// <param name="i_AuthenticatedUserCredentials">The credentials of the user creating the fault.</param>
        /// <param name="i_AccountID">The ID of the Account with the problem.</param>
        /// <param name="i_Description">The description of the fault.</param>
        /// <returns>The fault created.</returns>
        public int CreateFault(AuthenticatedUserCredentials i_AuthenticatedUserCredentials, int i_AccountID, string i_Description)
        {
            m_AuthenticationService.ValidateAuthentication(i_AuthenticatedUserCredentials);

            DataRow rowOfSearchingUser = GetRowOfUser(i_AuthenticatedUserCredentials.ID);

            eUserPermissions userPermissions = (eUserPermissions)int.Parse(rowOfSearchingUser["Permissions"].ToString());

            if (!((userPermissions & eUserPermissions.CreateFault) == eUserPermissions.CreateFault))
            {
                throw new NotEnoughPrivilagesException(i_AuthenticatedUserCredentials.ID);
            }

            DateTime date = DateTime.Now;

            string commandString = string.Format(
                "INSERT INTO Faults (UserID, AccountID, FaultDate, Description, IsSolved) VALUES ('{0}', {1}, '{2}', '{3}', 'False');",
                i_AuthenticatedUserCredentials.ID,
                i_AccountID,
				date.ToString("yyyy/MM/dd hh:mm:ss"),
                i_Description);

            DBService.Instance.ExecuteCommand(commandString);

            string selectString = string.Format(
                "SELECT FaultID FROM Faults WHERE UserID = '{0}' AND FaultDate = '{1}' AND Description LIKE '{2}' AND IsSolved = 'False';",
                i_AuthenticatedUserCredentials.ID,
                date.ToString("yyyy/MM/dd hh:mm:ss"),
                i_Description);

            DataTable faultsTable = DBService.Instance.PerformQuery(selectString);

            return int.Parse(faultsTable.Rows[0]["FaultID"].ToString());
        }

        /// <summary>
        /// Updates a fault.
        /// </summary>
        /// <param name="i_AuthenticatedUserCredentials">The credentials of the user performing the operation.</param>
        /// <param name="i_Fault">The information of the fault.</param>
        public void UpdateFault(AuthenticatedUserCredentials i_AuthenticatedUserCredentials, Fault i_Fault)
        {
            m_AuthenticationService.ValidateAuthentication(i_AuthenticatedUserCredentials);

            DataRow rowOfSearchingUser = GetRowOfUser(i_AuthenticatedUserCredentials.ID);

            eUserPermissions userPermissions = (eUserPermissions)int.Parse(rowOfSearchingUser["Permissions"].ToString());

            if (!((userPermissions & eUserPermissions.UpdateFaultInfo) == eUserPermissions.UpdateFaultInfo))
            {
                throw new NotEnoughPrivilagesException(i_AuthenticatedUserCredentials.ID);
            }

            string commandString = string.Format(
                "UPDATE Faults SET UserID = '{0}', AccountID = '{1}', FaultDate = '{2}', Description = '{3}', IsSolved = '{4}' WHERE FaultID = '{5}';",
                i_Fault.UserID,
                i_Fault.AccountID,
				i_Fault.FaultDate.ToString("yyyy/MM/dd hh:mm:ss"),
                i_Fault.Description,
                i_Fault.IsSolved,
                i_Fault.FaultID);

            DBService.Instance.ExecuteCommand(commandString);
        }

		/// <summary>
		/// Solve a fault.
		/// </summary>
		/// <param name="i_AuthenticatedUserCredentials">The credentials of the user performing the operation.</param>
		/// <param name="i_Fault">The information of the fault.</param>
		public void SolveFault(AuthenticatedUserCredentials i_AuthenticatedUserCredentials, int i_FaultID)
		{
			m_AuthenticationService.ValidateAuthentication(i_AuthenticatedUserCredentials);

			DataRow rowOfSearchingUser = GetRowOfUser(i_AuthenticatedUserCredentials.ID);

			eUserPermissions userPermissions = (eUserPermissions)int.Parse(rowOfSearchingUser["Permissions"].ToString());

			if (!((userPermissions & eUserPermissions.UpdateFaultInfo) == eUserPermissions.UpdateFaultInfo))
			{
				throw new NotEnoughPrivilagesException(i_AuthenticatedUserCredentials.ID);
			}

			string commandString = string.Format("UPDATE Faults SET IsSolved = 'true' WHERE FaultID = '{0}';", i_FaultID);

			DBService.Instance.ExecuteCommand(commandString);
		}


        private DataRow GetRowOfUser(int i_UserID)
        {
            DataTable table = DBService.Instance.PerformQuery(string.Format("SELECT UserID, Username, Permissions, Phone, MobilePhone, Email, Password FROM Users WHERE UserID = '{0}'", i_UserID));

            if (table.Rows.Count == 0)
            {
                throw new UserIDNotExistsException(i_UserID);
            }

            return table.Rows[0];
        }
    }
}
